home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 013 / cpuid.arc / CPUID.ASM next >
Assembly Source File  |  1986-04-24  |  19KB  |  584 lines

  1.     title    CPUID -- Determine CPU & NDP Type
  2.     page 58,122
  3.     name CPUID
  4.  
  5. COMMENT|
  6.  
  7. CPUID uniquely identifies each NEC & Intel CPU & NDP
  8.  
  9. Notes on Program Structure
  10.    This program uses four segments, two classes, and one group.  It
  11. demonstrates a useful technique for programmers who generate .COM
  12. programs.  In particular, it shows how to use segment classes to
  13. re-order segments, and how to eliminate the linker's warning message
  14. about the absence of a stack segment.
  15.    The correspondence between segments and classes is as follows:
  16.  
  17.     Segment     Class
  18.     -------     -----
  19.     STACK        prog
  20.     DATA        data
  21.     MDATA        data
  22.     CODE        prog
  23.  
  24.    The segments appear in the above order in the program source to
  25. avoid forward references in the CODE segment to labels in the
  26. DATA/MDATA segments.  However, because the STACK segment appears first
  27. in the file, it and all segments in the same class are made contiguous
  28. by the linker.    Thus they precede the DATA/MDATA segments in th the
  29. resulting .COM file because the letter are in a different class.  In
  30. this manner, although DATA and MDATA precede CODE in the source file,
  31. their order is swapped in the .COM file.  That way there is no need
  32. for an initial skip over the data areas to get to the CODE segment.
  33. As a side benefit, declaring a STACK segment (as the first segment in
  34. the source) also eliminates the linker's warning about that segment
  35. being missing.    Finally, all segments are declared to be in the same
  36. group so the linker can properly resolve offsets.
  37.    Note that if you re-assemble the code for any reason, it is
  38. important to use an assembler later than the IBM version 1.0.  That
  39. version has a numbers of bugs including an annoying habit of
  40. alphabetizing segment names in the .OBJ file.  If you use IBM MASM
  41. 2.0, be sure to specify /S to order the segments properly.
  42.    If the program reports results at variance with your knowledge of
  43. the system, please contact the author.
  44.  
  45. Environments tested in:
  46.  
  47.  
  48.            CPU Speed
  49. System        in MHz        CPU        NDP
  50. ---------------------------------------------------------
  51. IBM PC AT    6        Intel 80286    Intel 80287
  52. IBM PC AT    9        Intel 80286    Intel 80287
  53. IBM PC AT    6        Intel 80286    none
  54. IBM PC AT    8.5        Intel 80286    none
  55. IBM PC        4.77        Intel 8088    Intel 8087-3
  56. IBM PC        4.77        Intel 8088*    Intel 8087-3
  57. IBM PC XT    4.77        Intel 8088    none
  58. IBM PC XT    4.77        Intel 8088    Intel 8087-3
  59. COMPAQ        4.77        Intel 8088    none
  60. COMPAQ        4.77        NEC V20     none
  61. AT&T 6300    8        Intel 8086    Intel 8087-2
  62. AT&T 6300    8        NEC V20     Intel 8087-2
  63. TANDY 2000    8        Intel 80186    none
  64.  
  65. * = faulty CPU
  66.  
  67. Program structure:
  68.   Group PGROUP:
  69.   Stack   segment STACK, byte-aligned, stack,  class 'prog'
  70.   Program segment CODE,  byte-aligned, public, class 'prog'
  71.   Data      segment DATA,  byte-aligned, public, class 'data'
  72.   Data      segment MDATA, byte-aligned, public, class 'data'
  73.  
  74. Assembly requirements:
  75.   Use MASM 1.25 or later
  76.   With IBM's MASM 2.0 only, use /S to avoid
  77.     alphabetizing the segment names.
  78.   Use /r option to generate real NDP code.
  79.   MASM CPUID/r;         to convert .ASM to .OBJ
  80.   LINK CPUID;            to convert .OBJ to .EXE
  81.   EXE2BIN CPUID CPUID.COM    to convert .EXE to .COM
  82.   ERASE CPUID.EXE        to avoid executing .EXE
  83.  
  84.   Note that the linker doesn't warn about a missing stack segment.
  85.  
  86. Original code by:    Bob Smith    May 1985
  87.             Qualities, Inc.
  88.             8314 Thoreau Dr.
  89.             Bethesda, MD 20817
  90.  
  91.   Arthur Zechai suggested the technique to distinguish within the 808x
  92.   and 8018x families by exploiting the difference in the length of
  93.   their pre-fetch instruction queues.
  94. |
  95.     subttl    Structures, Records, Equates, and Macros
  96.     page
  97.  
  98. ARG_STR struc            ;
  99.     dw    ?        ; caller's BP
  100. ARG_OFF dw    ?        ; caller's offset
  101. ARG_SEG dw    ?        ;       segment
  102. ARG_FLG dw    ?        ;       flags
  103. ARG_STR ends            ;
  104.  
  105. ; Record to define bits in the CPU's & NDP's flags' registers
  106. CPUFLAGS record R0:1,NT:1,IOPL:2,OF:1,DF:1,IF:1,TF:1,SF:1,ZF:1,R1:1,AF:1,R2:1,PF:1,R3:1,CF:1
  107. NDPFLAGS record R4:1,IC:1,RC:2,PC:2,IEM:1,R5:1,PM:1,UM:1,OM:1,ZM:1,DM:1,IM:1
  108.  
  109. COMMENT|
  110. FLG_PIQL    Pre-fetch instruction queue length, 0 => 4-byte
  111.                             1 => 6-byte
  112. FLG_08        Intel 808x
  113. FLG_NEC     NEC V20 or V30
  114. FLG_18        Intel 8018x
  115. FLG_28        Intel 8028x
  116. FLG_87        Intel 8087
  117. FLG_287     Intel 80287
  118.  
  119. FLG_CERR    Faulty CPU
  120. FLG_NERR    Faulty NDP switch setting
  121. |
  122. FLG    record    RSVD:9,FLG_NERR:1,FLG_CERR:1,FLG_NDP:2,FLG_CPU:3
  123.  
  124. ; CPU-related flags
  125. FLG_PIQL equ    001b shl FLG_CPU
  126. FLG_08     equ    000b shl FLG_CPU
  127. FLG_NEC  equ    010b shl FLG_CPU
  128. FLG_18     equ    100b shl FLG_CPU
  129. FLG_28     equ    110b shl FLG_CPU
  130.  
  131. FLG_8088 equ    FLG_08
  132. FLG_8086 equ    FLG_08 or FLG_PIQL
  133. FLG_V20  equ    FLG_NEC
  134. FLG_V30  equ    FLG_NEC or FLG_PIQL
  135. FLG_80188 equ    FLG_18
  136. FLG_80186 equ    FLG_18 or FLG_PIQL
  137. FLG_80286 equ    FLG_28 or FLG_PIQL
  138.  
  139. ; NDP-related flags
  140. ;        00b shl FLG_NDP     Not present
  141. FLG_87    equ    01b shl FLG_NDP
  142. FLG_287 equ    10b shl FLG_NDP
  143.  
  144. BEL    equ    07h
  145. LF    equ    0Ah
  146. CR    equ    0Dh
  147. EOS    equ    '$'
  148.  
  149. POPFF    macro
  150.     local    L1,L2
  151.     jmp    short L2    ; Skip over IRET
  152. L1:
  153.     iret            ; pop the CS & IP pushed below along
  154.                 ; w. the flags, our original purpose
  155. L2:
  156.     push    cs        ; prepare for IRET by pushing CS
  157.     call    L1        ; push IP, jump to IRET
  158.     endm            ; POPFF macro
  159.  
  160. TAB    macro    TYP
  161.     push    bx        ; save for a moment
  162.     and    bx,mask FLG_&TYP ; isolate flags
  163.     mov    cl,FLG_&TYP    ; shift amount
  164.     shr    bx,cl        ; shift to low-order
  165.     shl    bx,1        ; times two to index table of words
  166.     mov    dx,TYP&MSG_TAB[bx] ; DS:DX ==> descriptive message
  167.     pop    bx        ; restore
  168.     mov    ah,09h        ; function code to display string
  169.     int    21h        ; request DOS service
  170.     endm            ; TAB macro
  171.     page
  172.  
  173. INT_VEC segment at 0        ; start INT_VEC segment
  174.     dd    ?        ; pointer to INT 00h
  175. INT01_OFF dw    ?        ; pointer to INT 01h
  176. INT01_SEG dw    ?        ;
  177. INT_VEC ends            ; end INT_VEC segment
  178.  
  179. PGROUP    group    STACK,CODE,DATA,MDATA
  180.  
  181. ; The following segment both positions class 'prog' segments lower in
  182. ; memory than others so the first byte of the resulting .COM file is
  183. ; in the CODE segment, as well as satisfies the LINKer's need to have
  184. ; a stack segment.
  185.  
  186. STACK    segment byte stack 'prog'       ; start of STACK segment
  187. STACK    ends                ; end of STACK segment
  188.  
  189. I11_REC record I11_PRN:2,I11_RSV1:2,I11_COM:3,I11_RSV2:1,I11_DISK:2,I11_VID:2,I11_RSV3:2,I11_NDP:1,I11_IPL:1
  190.  
  191. DATA    segment byte public 'data'      ; start DATA segment
  192.     assume    ds:PGROUP        ;
  193.  
  194. OLDINT01_VEC label dword        ; save area for original INT 01h handler
  195. OLDINT01_OFF dw ?            ;
  196. OLDINT01_SEG dw ?            ;
  197.  
  198. NDP_CW    label    word            ; save area for NDP control word
  199.     db    ?            ;
  200. NDP_CW_HI db    0            ; high byte of control word
  201. NDP_ENV dw    7 dup (?)        ; save area for NDP environment
  202.  
  203. DATA    ends
  204.  
  205.     subttl    Message Data Area
  206.     page
  207.  
  208. MDATA    segment byte public 'data'      ; start of MDATA segment
  209.     assume    ds:PGROUP        ;
  210.  
  211. MSG_START db    'CPUID    -- Version 1.0'
  212.       db    CR,LF,EOS
  213. MSG_8088 db    'CPU is an Intel 8088.',CR,LF,EOS
  214. MSG_8086 db    'CPU is an Intel 8086.',CR,LF,EOS
  215. MSG_V20  db    'CPU is an NEC V20.',CR,LF,EOS
  216. MSG_V30  db    'CPU is an NEC V30.',CR,LF,EOS
  217. MSG_80188 db    'CPU is an Intel 80188.',CR,LF,EOS
  218. MSG_80186 db    'CPU is an Intel 80186.',CR,LF,EOS
  219. MSG_UNK  db    'CPU is a maverick -- 80288??.',CR,LF,EOS
  220. MSG_80286 db    'CPU is an Intel 80286.',CR,LF,EOS
  221.  
  222. CPUMSG_TAB label word
  223.     dw    PGROUP:MSG_8088     ; 000 = Intel 8088
  224.     dw    PGROUP:MSG_8086     ; 000 = Intel 8086
  225.     dw    PGROUP:MSG_V20        ; 000 = NEC V20
  226.     dw    PGROUP:MSG_V30        ; 000 = NEC V30
  227.     dw    PGROUP:MSG_80188    ; 000 = Intel 80188
  228.     dw    PGROUP:MSG_80186    ; 000 = Intel 80186
  229.     dw    PGROUP:MSG_UNK        ; 000 = ?
  230.     dw    PGROUP:MSG_80286    ; 000 = Intel 80286
  231.  
  232. NDPMSG_TAB label word
  233.     dw    PGROUP:MSG_NDPX     ; 00 = No NDP
  234.     dw    PGROUP:MSG_8087     ; 00 = Intel 8087
  235.     dw    PGROUP:MSG_80287    ; 00 = Intel 80287
  236.  
  237. MSG_NDPX db    'NDP is not present.',CR,LF,EOS
  238. MSG_8087 db    'NDP is an Intel 8087.',CR,LF,EOS
  239. MSG_80287 db    'NDP is an Intel 80287.',CR,LF,EOS
  240.  
  241. CERRMSG_TAB label word
  242.     dw    PGROUP:MSG_CPUCK    ; 0 = CPU healthy
  243.     dw    PGROUP:MSG_CPUBAD    ; 0 = CPU faulty
  244.  
  245. MSG_CPUCK db    'CPU appears to be healthy.',CR,LF,EOS
  246. MSG_CPUBAD label byte
  247.     db    BEL,'*** CPU Incorrectly allows interrupts'
  248.     db    'after a change to SS ***',CR,LF
  249.     db    'It should be replaced with a more recent'
  250.     db    'version as it could crash the',CR,LF
  251.     db    'system at seemingly random times.',CR,LF,EOS
  252.  
  253. NERRMSG_TAB label word
  254.     dw    PGROUP:MSG_NDPSWOK    ; 0 = NDP switch set correctly
  255.     dw    PGROUP:MSG_NDPSWERR    ; 1 = NDP switch set incorrectly
  256.  
  257. MSG_NDPSWOK db    EOS            ; No message
  258. MSG_NDPSWERR label byte
  259.     db    '*** Although there is an NDP installed'
  260.     db    'on this system, the corresponding',CR,LF
  261.     db    'system board switch is not properly set.  '
  262.     db    'To correct this, flip switch 2 of',CR,LF
  263.     db    'switch block 1 on the system board.',CR,LF,EOS
  264.  
  265. MDATA    ends                ; end MDATA segment
  266.  
  267.     subttl    Main Routine
  268.     page
  269.  
  270. CODE    segment byte public 'prog'      ; start CODE segment
  271.     assume    cs:PGROUP,ds:PGROUP,es:PGROUP
  272.     org    100h            ; skip over PSP
  273.  
  274. INITIAL proc    near
  275.     mov    dx,offset ds:MSG_START    ; starting message
  276.     mov    ah,09h            ; function code to display string
  277.     int    21h            ;
  278.     call    CPUID            ; check the CPU's identity
  279.     TAB    CPU            ; display CPU results
  280.     TAB    NDP            ; display NDP results
  281.     TAB    CERR            ; display CPU ERR results
  282.     TAB    NERR            ; display NDP ERR results
  283.     ret                ; return to DOS
  284. INITIAL endp                ; end initial procedure
  285.  
  286.     subttl    CPUID Procedure
  287.     page
  288.  
  289. CPUID    proc    near            ; start CPUID procedure
  290.     assume    cs:PGROUP,ds:PGROUP,es:PGROUP
  291. COMMENT|
  292. This procedure determines the type of CPU and NDP (if any) in use.
  293. The possibilities include
  294. Intel 8086
  295. Intel 8088
  296. NEC   V20
  297. NEC   V30
  298. Intel 80186
  299. Intel 80188
  300. Intel 80286
  301. Intel 8087
  302. Intel 80287
  303.  
  304.    Also checked is whether or not the CPU allows interrupts after
  305. changing the SS segment register.  If the CPU does, it is faulty and
  306. should be replaced.
  307.    Further, if an NDP is installed, non-AT machines should have a
  308. system board switch set.  Such a discrepency is reported.
  309.    On exit, BX contains flag settings (as defined in FLG record) which
  310. the caller can check.  For example, to test for an Intel 80286, use
  311.  
  312.     and    bx,mask FLAG_CPU
  313.     cmp    bx,FLG_80286
  314.     je    ITSA286
  315. |
  316.  
  317.     irp    XX,<ax,cx,di,ds,es>    ; save registers
  318.     push    XX            ;
  319.     endm                ;
  320.  
  321. ; Test for 80286 -- this CPU executes PUSH SP by first storing SP on
  322. ; stack, then decrementing it.    Earlier CPUs decrement, THEN store.
  323.  
  324.     mov    bx,FLG_28        ; assume it's a 286
  325.     push    sp            ; only 286 pushes pre-push SP
  326.     pop    ax            ; get it back
  327.     cmp    ax,sp            ; check for same
  328.     je    CHECK_PIQL        ; they are, so it's a 286
  329.  
  330. ; Test for 80186/80188 -- 18x and 286 CPUs mask shift/rotate
  331. ; operations mod 32; earlier CPUs use all 8 bits of CL.
  332.  
  333.     mov    bx,FLG_18        ; assume it's a 8018x
  334.     mov    cl,32+1         ; 18x masks shift counts mod 32
  335.                     ; Note we can't use just 32 in CL
  336.     mov    al,0ffh         ; start with all bits set
  337.     shl    al,cl            ; shift one position in 18x
  338.     jnz    CHECK_PIQL        ; some bits still on,
  339.                     ; so it's a 18x, check PIQL
  340.  
  341.     mov    bx,FLG_NEC        ; assume it's an NEC V-series CPU
  342.     call    CHECK_NEC        ; see if it's an NEC chip
  343.     jcxz    CHECK_PIQL        ; good guess, check PIQL
  344.  
  345.     mov    bx,FLG_08        ; it's an 808x
  346.  
  347.     subttl    Check Length Of Pre-fetch Instruction Queue
  348.     page
  349.  
  350. COMMENT|
  351. Check the length of the pre-fetch instruction queue (PIQ).
  352. xxxx6 CPUs have a PUQ length of 6 bytes,
  353. xxxx8 CPUs   "  "  "     "   "  4   "
  354.  
  355. Self-modifying code is used to distinguish the two PIQ lengths.
  356. |
  357. CHECK_PIQL:
  358.     call    PIQL_SUB        ; handled via subroutine
  359.     jcxz    CHECK_ERR        ; if CX is 0, INC was not executed,
  360.                     ; hence PIQ length is 4
  361.     or    bx,FLG_PIQL        ; PIQ length is 6
  362.     subttl    Check for Allowing Interrupts after POP SS
  363.     page
  364. ; Test for faulty chip (allows interrupts after change to SS register)
  365. CHECK_ERR:
  366.     xor    ax,ax            ; prepare address
  367.                     ; interrupt vector segment
  368.     mov    ds,ax            ; DS points to segment 0
  369.     assume    ds:INT_VEC        ; tell the assembler
  370.     cli                ; nobody move while we swap
  371.     mov    ax,offset cs:INT01    ; point to our own handler
  372.     xchg    ax,INT01_OFF        ; get and swap segment
  373.     mov    OLDINT01_OFF,ax     ; save to restore later
  374.     mov    ax,cs            ; our handler's segment
  375.     xchg    ax,INT01_SEG        ; get and swap segment
  376.     mov    OLDINT01_SEG,ax     ; save to restore later
  377.  
  378. ; Note we continue with interrupts disabled to avoid
  379. ; an external interrupt occuring during this test.
  380.     mov    cx,1            ; initialize a register
  381.     push    ss            ; save SS to store back into itself
  382.     pushf                ; move flags
  383.     pop    ax            ; ...into AX
  384.     or    ax,mask TF        ; set trap flag
  385.     push    ax            ; place onto stack
  386.     POPFF                ; ...and then into effect
  387.                     ; some CPUs effect the trap flag
  388.                     ; immediately, some
  389.                     ; wait one instruction
  390.     nop                ; allow interrupt to take effect
  391. POST_NOP:
  392.     pop    ss            ; change the stack segment register
  393.                     ;  (to itself)
  394.     dec    cx            ; normal CPUs execute this instruction
  395.                     ;   before recognizing the single-step
  396.                     ;   interrupt
  397.     hlt                ; we never get here
  398. INT01:
  399. ; Note IF=TF=0
  400. ; if we're stopped at or before POST_NOP, continue on
  401.  
  402.     push    bp            ; prepare to address the stack
  403.     mov    bp,sp            ; hello, mr. stack
  404.     cmp    [bp].ARG_OFF,offset cs:POST_NOP ; check offset
  405.     pop    bp            ; restore
  406.     ja    INT01_DONE        ; we're done
  407.     iret                ; return to caller
  408. INT01_DONE:
  409. ; restore old INT 01h handler
  410.     les    ax,OLDINT01_VEC     ; ES:AX ==> old INT 01h handler
  411.     assume    es:nothing        ; tell the assembler
  412.     mov    INT01_OFF,ax        ; restore offset
  413.     mov    INT01_SEG,es        ; .. and segment
  414.     sti                ; allow interrupts again (IF=1)
  415.     add    sp,3*2            ; strip IP, CS, and Flags from stack
  416.     push    cs            ; set-up DS for code below
  417.     pop    ds            ;
  418.     assume    ds:PGROUP        ; tell the assembler
  419.     jcxz    CHECK_NDP        ; if CX is 0, the DEC CX was executed,
  420.                     ; and the CPU is OK
  421.     or    bx,mask FLG_CERR    ; it's a faulty chip
  422.  
  423.     subttl    Check for Numeris Data Processor
  424.     page
  425.  
  426. COMMENT|
  427.    Test for a Numeric Data Processor -- Intel 8087 or 80287.  The
  428. technique used is passive -- it leaves the NDP in the same state in
  429. which it is found.
  430. |
  431. CHECK_NDP:
  432.     cli                ; protect FNSTENV
  433.     fnstenv NDP_ENV         ; if NDP present, save
  434.                     ;   current environment,
  435.                     ;   otherwise, this instruction
  436.                     ;   is ignored
  437.     mov    cx,50/7         ; cycle this many times
  438.     loop    $            ; wait for result to be stored
  439.     sti                ; allow interrupts
  440.     fninit                ; initialize processor to known state
  441.     jmp    short $+2        ; wait for initialization
  442.     fnstcw    NDP_CW            ; save control word
  443.     jmp    short $+2        ; wait for result to be stored
  444.     jmp    short $+2        ;
  445.     cmp    NDP_CW_HI,03h        ; check for NDP initial control word
  446.     jne    CPUID_EXIT        ; no NDP installed
  447.     int    11h            ; get equipment flags into AX
  448.     test    ax,mask I11_NDP     ; check NDP installed bit
  449.     jnz    CHECK_NDP1        ; it's correctly set
  450.     or    bx,mask FLG_NERR    ; mask as in error
  451. CHECK_NDP1:
  452.     and    NDP_CW,not mask IEM    ; enable interrupts
  453.                     ;   (IEM=0, 8087 only)
  454.     fldcw    NDP_CW            ; reload control word
  455.     fdisi                ; disable interrupts (IEM=1) on 8087,
  456.                     ; ignored by 80287
  457.     fstcw    NDP_CW            ; save control word
  458.     fldenv    NDP_ENV         ; restore original NDP environment
  459.                     ; no need to wait
  460.                     ; for environment to be loaded
  461.     test    NDP_CW,mask IEM     ; check interrupt enable mask
  462.                     ;   (8087 only)
  463.     jnz    CPUID_8087        ; it changed, hence NDP is 8087
  464.     or    bx,FLG_287        ; NDP is an 80287
  465.     jmp    short CPUID_EXIT    ; exit with flags in BX
  466. CPUID_8087:
  467.     or    bx,FLG_87        ; NDP is an 8087
  468. CPUID_EXIT:
  469.     irp    XX,<es,ds,di,cx,ax>    ; restore registers
  470.     pop    XX            ;
  471.     endm                ;
  472.     assume    ds:nothing,es:nothing
  473.     ret                ; return to caller
  474.  
  475. CPUID    endp                ; end CPUID procedure
  476.  
  477.     subttl    Check for NEC V20/V30
  478.     page
  479.  
  480. CHECK_NEC proc    near
  481.  
  482. COMMENT|
  483.    The NEC V20/V30 CPUs are very compatible with the 8088/8086.
  484. The only point of "incompatibility" is that they do not contain a bug
  485. found in the Intel CPUs.  Specifically, the NEC CPUs correctly restart
  486. an interrupted multi-prefix string instruction at the start of the
  487. instruction.  The Intel CPUs incorrectly restart it in the middle of
  488. the instruction.  This routine tests for that situation by executing
  489. such an instruction for a sufficiently long perion of time for a timer
  490. interrupt to occur.  If at the end of the instruction, CX is zero,
  491. it must be an NEC CPU; if not, it's an Intel CPU.
  492.    Note that we're counting on the timer interrupt to do its thing
  493. every 18.2 times per second.
  494.    Here;s a worst case analysis: An Intel 8088/8086 executes 65535
  495. iterations of LODSB ES:[SI] in 2+9+13*65535 = 851,966 clock ticks.  If
  496. the Intel 8088/8086 is running at 10 MHz, each clock tick is 100
  497. nanoseconds, hence the entire operation takes 85 milliseconds.    If the
  498. timer is running at normal speed, it interrupts the CPU every 55 ms
  499. and so should interrupt the repeated string instruction at least once.
  500. |
  501.     mov    cx,0ffffh        ; move a lot of data
  502.     sti                ; ensure timer enabled
  503.  
  504. ; execute multi-prefix instruction.  Note that the value of ES as
  505. ; well as the direction flag setting is irrelevant.
  506.  
  507.     push    ax            ; save registers
  508.     push    si            ;
  509.     rep    lods byte ptr es:[si]    ;
  510.     pop    si            ; restore
  511.     pop    ax            ;
  512.  
  513. ; on exit, if CX is zero, it's an NEC CPU, otherwise it's an Intel CPU
  514.     ret                ; return to caller
  515. CHECK_NEC endp
  516.  
  517.     subttl    Pre-fetch Instruction Queue Subroutine
  518.     page
  519.  
  520. PIQL_SUB proc    near
  521.  
  522. COMMENT|
  523.    This subroutine discerns the length of the CPU's pre-fetch
  524. instruction queue (PIQ).
  525.    The technique used is to first ensure that the PIQ is full, then
  526. change an instruction which should be in a 6-byte PIQ but not in a
  527. 4-byte PIQ.  Then, if the original instruction is executed, the PIQ
  528. is 6 bytes long; if the new instruction is executed, PIQ length is 4.
  529.    We ensure the PIQ is full by executing an instruction which takes
  530. long enough so that the BUS Interface Unit (BIU) can fill the PIQ
  531. while the instruction is executing.
  532.    Specifically, for all but the last STOSB, we're simply marking time
  533. waiting for the BIU to fill the PIQ.  The last STOSB actually changes
  534. the instruction.  By that time, the original instruction should be in
  535. a six-byte PIQ but not a four-byte PIQ.
  536. |
  537.     assume    cs:PGROUP,es:PGROUP
  538.  
  539. @REP    equ    3            ; repeat the store this many times
  540.     std                ; store backwards
  541.     mov    di,offset es:LAB_INC+@REP-1 ; change the instructions
  542.                         ;    at ES:DI
  543.                         ; and preceding
  544.     mov    al,ds:LAB_STI    ; change to a STI
  545.     mov    cx,@REP     ; give the BIU time
  546.                 ;   to pre-fetch instructions
  547.     cli            ; ensure interrupts are disabled,
  548.                 ;   otherwise a timer tick
  549.                 ;   could change the PIQ filling
  550.     rep    stosb        ; change the instruction
  551.                 ; during execution of this intruction the BIU
  552.                 ; is refilling the PIQ.  The current
  553.                 ; instruction is no longer in the PIQ.
  554.                 ; Note at end, CX is 0
  555. ; the PIQ begins filling here
  556.     cld            ; restore direction flag
  557.     nop            ; PIQ filler
  558.     nop            ;
  559.     nop            ;
  560.  
  561. ; The following instruction is beyond a four-byte-PIQ CPU's reach,
  562. ; but within that of a six-byte-PIQ CPU.
  563.  
  564. LAB_INC label    byte
  565.     inc    cx        ; executed only if PIQ length is 6
  566.  
  567. LAB_STI label    byte
  568.     rept    @REP-1
  569.     sti            ; restore interrupts
  570.     endm
  571.     ret            ; return to caller
  572.     assume    ds:nothing,es:nothing
  573. PIQL_SUB endp            ; end PIQL_SUB procedure
  574.  
  575. CODE    ends            ; end CODE segment
  576.  
  577.     if1
  578.  %OUT Pass 1 complete
  579.     else
  580.  %OUT Pass 2 complete
  581.     endif
  582.  
  583.     end    INITIAL     ; end CPUID module
  584.